﻿using AutoMapper;
using ManagementApi.Authorizers;
using ManagementApi.Models;
using ManagementApi.Models.Project;
using ManagementApi.Mvc;
using ManagementApi.Mvc.ApplicationContexts;
using ManagementApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Modules;
using Modules.Common;
using Modules.Corp;
using Modules.Project;
using Modules.System.Dept;
using Modules.System.User;
using NPOI.SS.UserModel;
using PmSoft.Exceptions;
using PmSoft.Utilities;

namespace ManagementApi.Controllers.ManagerPannel
{
    /// <summary>
    /// 项目管理员-保证金账户
    /// </summary>
    [Route("/api/v1/manager/guarantee")]
    [ApiController]
    [Authorize(Roles = "Manager")]
    public class ProjectGuaranteeController : ControllerBase
    {
        private readonly ProjectGuaranteeAccountService projectGuaranteeAccountService;
        private readonly ProjectGuaranteePaymentService projectGuaranteePaymentService;
        private readonly ProjectGuaranteeRefundService projectGuaranteeRefundService;
        private readonly ProjectGuaranteeIncomeService projectGuaranteeIncomeService;
        private readonly ProjectGuaranteeExpenditureService projectGuaranteeExpenditureService;
        private readonly ProjectGuaranteeExpenditureDetailService projectGuaranteeExpenditureDetailService;
        private readonly ProjectManagerApplicationContext applicationContext;
        private readonly UserService userService;
        private readonly DeptService deptService;
        private readonly SerialNumberService serialNumberService;
        private readonly ProjectService projectService;
        private readonly CorpService corpService;
        private readonly IMapper mapper;
        private readonly ProjectOwnAuthorizer ownAuthorizer;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="projectGuaranteeAccountService"></param>
        /// <param name="applicationContext"></param>
        /// <param name="mapper"></param>
        /// <param name="projectGuaranteePaymentService"></param>
        /// <param name="projectGuaranteeRefundService"></param>
        /// <param name="projectGuaranteeIncomeService"></param>
        /// <param name="projectGuaranteeExpenditureService"></param>
        /// <param name="projectGuaranteeExpenditureDetailService"></param>
        /// <param name="userService"></param>
        /// <param name="deptService"></param>
        /// <param name="serialNumberService"></param>
        /// <param name="projectService"></param>
        /// <param name="corpService"></param>
        /// <param name="ownAuthorizer"></param>
        public ProjectGuaranteeController(ProjectGuaranteeAccountService projectGuaranteeAccountService, ProjectManagerApplicationContext applicationContext, IMapper mapper,
            ProjectGuaranteePaymentService projectGuaranteePaymentService, ProjectGuaranteeRefundService projectGuaranteeRefundService,
            ProjectGuaranteeIncomeService projectGuaranteeIncomeService, ProjectGuaranteeExpenditureService projectGuaranteeExpenditureService,
            ProjectGuaranteeExpenditureDetailService projectGuaranteeExpenditureDetailService,
            UserService userService, DeptService deptService, SerialNumberService serialNumberService, ProjectService projectService,
            CorpService corpService,
            ProjectOwnAuthorizer ownAuthorizer
            )
        {
            this.projectGuaranteeAccountService = projectGuaranteeAccountService;
            this.applicationContext = applicationContext;
            this.mapper = mapper;
            this.deptService = deptService;
            this.serialNumberService = serialNumberService;
            this.projectService = projectService;
            this.corpService = corpService;
            this.projectGuaranteePaymentService = projectGuaranteePaymentService;
            this.projectGuaranteeRefundService = projectGuaranteeRefundService;
            this.projectGuaranteeExpenditureService = projectGuaranteeExpenditureService;
            this.projectGuaranteeIncomeService = projectGuaranteeIncomeService;
            this.projectGuaranteeExpenditureDetailService = projectGuaranteeExpenditureDetailService;
            this.userService = userService;
            this.ownAuthorizer = ownAuthorizer;
        }

        #region 保证金账户

        /// <summary>
        /// 保证金账户表单数据
        /// </summary>
        /// <param name="id">角色ID</param>
        /// <returns></returns>
        [HttpGet("account/{id}")]
        public ApiResult<ProjectGuaranteeAccountDto> GetProjectGuaranteeAccount(int id)
        {
            var user = userService.GetUser(applicationContext.CurrentUser?.Id ?? 0);
            if (user == null) return ApiResult<ProjectGuaranteeAccountDto>.Failure("未查询到当前登录用户信息");
            var dept = deptService.GetSysDept(user.DeptId ?? 0);
            if (dept == null) return ApiResult<ProjectGuaranteeAccountDto>.Failure("未查询到当前登录用户主管单位信息");

            ProjectGuaranteeAccountDto projectGuaranteeAccountDto = new ProjectGuaranteeAccountDto();
            if (id == 0)
            {
                projectGuaranteeAccountDto.RsName = dept.Name;
                projectGuaranteeAccountDto.Code = serialNumberService.GetNumAndSerialNumber(SerialNumberType.保证金).Code;
                return ApiResult<ProjectGuaranteeAccountDto>.Success(projectGuaranteeAccountDto);
            }
            ProjectGuaranteeAccount? data = projectGuaranteeAccountService.GetProjectGuaranteeAccount(id);
            if (data == null)
                return ApiResult<ProjectGuaranteeAccountDto>.Failure("保证金账户不存在");

            return ApiResult<ProjectGuaranteeAccountDto>.Success(data.AsDto());
        }

        /// <summary>
        /// 获取保证金账户列表(分页)
        /// </summary>
        /// <param name="query">查询条件</param>
        /// <param name="pageNum">页码</param>
        /// <param name="pageSize">每页数量</param>
        /// <returns></returns>
        [HttpGet("account/page")]
        public ApiResult<PageDto<ProjectGuaranteeAccountDto>> QueryProjectGuarantees([FromQuery] ProjectGuaranteeAccountQuery query, int pageNum = 1, int pageSize = 20)
        {
            var data = projectGuaranteeAccountService.GetProjectGuaranteeAccounts(query, pageNum, pageSize);
            var dtos = data.AsPageDto(source => { return source.AsDto(); });
            return ApiResult<PageDto<ProjectGuaranteeAccountDto>>.Success(dtos);
        }

        #endregion 保证金账户

        #region 保证金缴纳信息

        /// <summary>
        /// 新增保证金账户
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        [HttpPost("payment/create")]
        public ApiResult CreateProjectGuaranteePayment(ProjectGuaranteePaymentEditForm form)
        {
            if (projectGuaranteePaymentService.ExistProjectGuaranteePayment(applicationContext.CurrentProjectInfo.ProjectId))
            {
                return ApiResult.Failure("该项目已存在保证金账户");
            }
            var project = projectService.GetProjectInfo(applicationContext.CurrentProjectInfo.ProjectId);
            if (project == null) return ApiResult.Failure("当前项目不存在");
            var corp = corpService.GetCorpInfo(project.LumpContractorCorpId);
            if (corp == null) return ApiResult.Failure("当前项目总承包不存在");

            ProjectGuaranteePayment entity = new();
            mapper.Map(form, entity);
            entity.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
            entity.PaymentName = corp.Name;
            entity.PaymentCode = corp.Code;
            entity.Creator = applicationContext.CurrentUser?.Name ?? string.Empty;
            entity.CreateTime = DateTime.Now;
            projectGuaranteePaymentService.Create(entity, form.Attachments.Select(i => i.AttachmentId));
            return ApiResult.Success("创建成功");
        }

        /// <summary>
        /// 编辑保证金账户
        /// </summary>
        /// <param name="id"></param>
        /// <param name="form"></param>
        /// <returns></returns>
        [HttpPut("payment/update")]
        public ApiResult UpdateProjectGuaranteePayment(ProjectGuaranteePaymentEditForm form)
        {
            var entity = projectGuaranteePaymentService.GetProjectGuaranteePayment(form.Id ?? 0);
            if (entity == null) return ApiResult.Failure("保证金不存在");

            #region 验证数据权限

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(entity.ProjectId);

            #endregion 验证数据权限

            if (entity.Status != 保证金状态.待核实 && entity.Status != 保证金状态.审核不通过) return ApiResult.Failure("审核通过的保证金不允许修改");
            mapper.Map(form, entity);
            entity.Status = 保证金状态.待核实;
            projectGuaranteePaymentService.Update(entity, form.Attachments.Select(i => i.AttachmentId));
            return ApiResult.Success("修改成功");
        }

        /// <summary>
        /// 删除保证金账户
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        [HttpDelete("payment/{ids}")]
        public ApiResult DeleteProjectGuaranteePayment(string ids)
        {
            var entitys = projectGuaranteePaymentService.GetProjectGuaranteePayments(ValueUtility.ConvertToList<int>(ids));

            #region 验证数据权限

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(entitys.Select(i => i.ProjectId));

            #endregion 验证数据权限

            projectGuaranteePaymentService.Deleted(ValueUtility.ConvertToList<int>(ids));
            return ApiResult.Success("删除成功");
        }

        /// <summary>
        /// 保证金账户表单数据
        /// </summary>
        /// <param name="id">角色ID</param>
        /// <returns></returns>
        [HttpGet("payment/{id}")]
        public ApiResult<ProjectGuaranteePaymentDto> GetProjectGuaranteePayment(int id)
        {
            var project = projectService.GetProjectInfo(applicationContext.CurrentProjectInfo.ProjectId);
            if (project == null) return ApiResult<ProjectGuaranteePaymentDto>.Failure("当前项目不存在");
            var corp = corpService.GetCorpInfo(project.LumpContractorCorpId);
            if (corp == null) return ApiResult<ProjectGuaranteePaymentDto>.Failure("当前项目总承包不存在");
            ProjectGuaranteePaymentDto dto = new ProjectGuaranteePaymentDto();

            if (id == 0)
            {
                dto.Payable = Math.Round((project.ContractAmount ?? 0) / 100 * 3, 2);
                dto.PaymentName = corp.Name;
                dto.PaymentCode = corp.Code;
                return ApiResult<ProjectGuaranteePaymentDto>.Success(dto);
            }
            var data = projectGuaranteePaymentService.GetProjectGuaranteePayment(id);
            if (data == null)
                return ApiResult<ProjectGuaranteePaymentDto>.Failure("保证金账户不存在");

            return ApiResult<ProjectGuaranteePaymentDto>.Success(data.AsDto(applicationContext));
        }

        /// <summary>
        /// 获取保证金账户列表(分页)
        /// </summary>
        /// <param name="query">查询条件</param>
        /// <param name="pageNum">页码</param>
        /// <param name="pageSize">每页数量</param>
        /// <returns></returns>
        [HttpGet("payment/page")]
        public ApiResult<PageDto<ProjectGuaranteePaymentDto>> GetProjectGuaranteePayments([FromQuery] ProjectGuaranteePaymentQuery query, int pageNum = 1, int pageSize = 20)
        {
            if (!query.ProjectId.HasValue) query.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
            var data = projectGuaranteePaymentService.GetProjectGuaranteePayments(query, pageNum, pageSize);
            var dtos = data.AsPageDto(source => { return source.AsDto(applicationContext); });
            return ApiResult<PageDto<ProjectGuaranteePaymentDto>>.Success(dtos);
        }

        /// <summary>
        /// 通过保证金账户获取所有的缴纳信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("payment/list/{id}")]
        public ApiResult<IEnumerable<ProjectGuaranteePaymentDto>> GetProjectGuaranteePaymentsByAccountId(int id)
        {
            var data = projectGuaranteePaymentService.GetProjectGuaranteePayments(id);
            List<ProjectGuaranteePaymentDto> dtos = new();
            foreach (var item in data)
            {
                dtos.Add(item.AsDto(applicationContext));
            }
            return ApiResult<IEnumerable<ProjectGuaranteePaymentDto>>.Success(dtos);
        }

        ///// <summary>
        ///// 审核保证金
        ///// </summary>
        ///// <param name="auditForm"></param>
        ///// <returns></returns>
        //[HttpPut("payment/audit")]
        //public ApiResult AuditProjectGuaranteePayment(ProjectGuaranteePaymentAuditForm auditForm)
        //{
        //    var projectGuaranteePayment = projectGuaranteePaymentService.GetProjectGuaranteePayment(auditForm.Id);
        //    if (projectGuaranteePayment == null) return ApiResult.Failure("审核的缴纳账户不存在");
        //    projectGuaranteePayment.Status = auditForm.Status;
        //    projectGuaranteePayment.CheckReason = auditForm.CheckReason;
        //    projectGuaranteePaymentService.Update(projectGuaranteePayment, new List<string>());
        //    return ApiResult.Success("操作成功");
        //}

        #endregion 保证金缴纳信息

        #region 保证金退还信息

        /// <summary>
        /// 申请退还
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        [HttpPost("refund/apply")]
        public ApiResult ApplyProjectGuaranteeRefund(ProjectGuaranteeRefundEditForm form)
        {
            var payment = projectGuaranteePaymentService.GetProjectGuaranteePaymentByProjectId(applicationContext.CurrentProjectInfo.ProjectId) ?? throw new BusinessException("无需退还保证金");
            if (payment.GuaranteeAccountType != 保证金类型.保证金账户 || payment.HasReturn) throw new BusinessException("无需退还保证金");
            if (payment.Id != form.GuaranteePaymentId) throw new BusinessException("表单信息错误.退还缴纳信息不一致");
            if(form.ApplyAmount>payment.Paid) throw new BusinessException("申请退还金额不能大于可退还金额");

            #region 验证数据权限

            ownAuthorizer.CheckAndThrowIfOwnAuthenticationFailed(payment.ProjectId);

            #endregion 验证数据权限

            var entity = projectGuaranteeRefundService.GetProjectGuaranteeRefundByGuaranteePaymentId(form.GuaranteePaymentId);
            if (entity == null)
            {
                entity = new ProjectGuaranteeRefund();
                mapper.Map(form, entity);
                entity.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
                entity.Status = 保证金退还状态.待审核;
                entity.CreateTime = DateTime.Now;
                entity.GuaranteePaymentId = payment.Id;
                projectGuaranteeRefundService.Create(entity);
                return ApiResult.Success("操作成功");
            }
            if (entity.Status == 保证金退还状态.已退还) return ApiResult.Failure("已退还,不需要再次申请");
            mapper.Map(form, entity);
            entity.Status = 保证金退还状态.待审核;
            projectGuaranteeRefundService.Update(entity);
            return ApiResult.Success("操作成功");
        }

        /// <summary>
        /// 获取需要退还的保证金paymentId
        /// </summary>
        /// <returns></returns>
        [HttpGet("refund/need_apply")]
        public ApiResult<int> GetNeedRefundPayment()
        {
            var payment = projectGuaranteePaymentService.GetProjectGuaranteePaymentByProjectId(applicationContext.CurrentProjectInfo.ProjectId) ?? throw new BusinessException("无需退还保证金");
            if (payment.GuaranteeAccountType != 保证金类型.保证金账户 || payment.HasReturn) throw new BusinessException("无需退还保证金");
            return ApiResult<int>.Success(payment.Id);
        }

        /// <summary>
        /// 退还信息详情
        /// </summary>
        /// <param name="id">退还记录Id</param>
        /// <param name="guaranteePaymentId">角色ID</param>
        /// <returns></returns>
        [HttpGet("refund/{id}")]
        public ApiResult<ProjectGuaranteeRefundDto> GetProjectGuaranteeRefund(int id, int guaranteePaymentId)
        {
            if (id == 0)
            {
                ProjectGuaranteeRefund refund = new ProjectGuaranteeRefund();
                refund.GuaranteePaymentId = guaranteePaymentId;
                refund.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
                return ApiResult<ProjectGuaranteeRefundDto>.Success(refund.AsDto(applicationContext));
            }
            var data = projectGuaranteeRefundService.GetProjectGuaranteeRefund(id);
            if (data == null)
                return ApiResult<ProjectGuaranteeRefundDto>.Failure("退还记录不存在");

            return ApiResult<ProjectGuaranteeRefundDto>.Success(data.AsDto(applicationContext));
        }

        /// <summary>
        /// 获取保证金账户列表(分页)
        /// </summary>
        /// <param name="query">查询条件</param>
        /// <param name="pageNum">页码</param>
        /// <param name="pageSize">每页数量</param>
        /// <returns></returns>
        [HttpGet("refund/page")]
        public ApiResult<PageDto<ProjectGuaranteeRefundDto>> GetProjectGuaranteeRefunds([FromQuery] ProjectGuaranteeRefundQuery query, int pageNum = 1, int pageSize = 20)
        {
            var data = projectGuaranteeRefundService.GetProjectGuaranteeRefunds(query, pageNum, pageSize);
            var dtos = data.AsPageDto(source => { return source.AsDto(applicationContext); });
            return ApiResult<PageDto<ProjectGuaranteeRefundDto>>.Success(dtos);
        }

        #endregion 保证金退还信息

        #region 账户收入信息

        /// <summary>
        /// 收入信息表单数据
        /// </summary>
        /// <param name="id">角色ID</param>
        /// <returns></returns>
        [HttpGet("income/{id}")]
        public ApiResult<ProjectGuaranteeIncomeDto> GetProjectGuaranteeIncome(int id)
        {
            ProjectGuaranteeIncome? data = projectGuaranteeIncomeService.GetProjectGuaranteeIncome(id);
            if (data == null)
                return ApiResult<ProjectGuaranteeIncomeDto>.Failure("收支信息不存在");

            return ApiResult<ProjectGuaranteeIncomeDto>.Success(data.AsDto(applicationContext));
        }

        /// <summary>
        /// 获取收入信息列表(分页)
        /// </summary>
        /// <param name="query">查询条件</param>
        /// <param name="pageNum">页码</param>
        /// <param name="pageSize">每页数量</param>
        /// <returns></returns>
        [HttpGet("income/page")]
        public ApiResult<PageDto<ProjectGuaranteeIncomeDto>> QueryProjectGuaranteeIncomes([FromQuery] ProjectGuaranteeIncomeQuery query, int pageNum = 1, int pageSize = 20)
        {
            query.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
            var data = projectGuaranteeIncomeService.GetProjectGuaranteeIncomes(query, pageNum, pageSize);
            var dtos = data.AsPageDto(source => { return source.AsDto(applicationContext); });
            return ApiResult<PageDto<ProjectGuaranteeIncomeDto>>.Success(dtos);
        }

        /// <summary>
        /// 导出Excel（收入明细）
        /// </summary>
        /// <returns></returns>
        [HttpGet("income/export")]
        public IActionResult ExportProjectGuaranteeIncomes([FromQuery] ProjectGuaranteeIncomeQuery query, int pageIndex = 1, int pageSize = 9999)
        {
            query.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
            var data = projectGuaranteeIncomeService.GetProjectGuaranteeIncomes(query, pageIndex, pageSize);
            IWorkbook workbook = ExcelExportHelper.ExportToExcel(data.AsExportDto().ToList());

            // 将工作簿保存到内存流中
            using (MemoryStream memoryStream = new MemoryStream())
            {
                workbook.Write(memoryStream);
                // 返回文件内容结果
                return File(memoryStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"收入明细_{DateTime.Now.ToString("yyyyMMddHHmmss ")}.xlsx");
            }
        }

        #endregion 账户收入信息

        #region 账户支出信息

        /// <summary>
        /// 支出信息表单数据
        /// </summary>
        /// <param name="id">角色ID</param>
        /// <returns></returns>
        [HttpGet("expenditure/{id}")]
        public ApiResult<ProjectGuaranteeExpenditureDto> GetProjectGuaranteeExpenditure(int id)
        {
            ProjectGuaranteeExpenditure? data = projectGuaranteeExpenditureService.GetProjectGuaranteeExpenditure(id);
            if (data == null)
                return ApiResult<ProjectGuaranteeExpenditureDto>.Failure("支出信息不存在");

            return ApiResult<ProjectGuaranteeExpenditureDto>.Success(data.AsDto(applicationContext));
        }

        /// <summary>
        /// 获取支出明细详情
        /// </summary>
        /// <param name="guaranteeExpenditureId">明细Id</param>
        /// <returns></returns>
        [HttpGet("expenditure/detail/{guaranteeExpenditureId}")]
        public ApiResult<IEnumerable<ProjectGuaranteeExpenditureDetail>> GetProjectGuaranteeExpenditureDetail(int guaranteeExpenditureId)
        {
            var data = projectGuaranteeExpenditureDetailService.GetProjectGuaranteeExpenditureDetailsByGuaranteeExpenditureId(guaranteeExpenditureId);
            return ApiResult<IEnumerable<ProjectGuaranteeExpenditureDetail>>.Success(data);
        }

        /// <summary>
        /// 获取支出信息列表(分页)
        /// </summary>
        /// <param name="query">查询条件</param>
        /// <param name="pageNum">页码</param>
        /// <param name="pageSize">每页数量</param>
        /// <returns></returns>
        [HttpGet("expenditure/page")]
        public ApiResult<PageDto<ProjectGuaranteeExpenditureDto>> QueryProjectGuaranteeExpenditure([FromQuery] ProjectGuaranteeExpenditureQuery query, int pageNum = 1, int pageSize = 20)
        {
            query.ProjectId = applicationContext.CurrentProjectInfo.ProjectId;
            var data = projectGuaranteeExpenditureService.GetProjectGuaranteeExpenditures(query, pageNum, pageSize);
            var dtos = data.AsPageDto(source => { return source.AsDto(applicationContext); });
            return ApiResult<PageDto<ProjectGuaranteeExpenditureDto>>.Success(dtos);
        }

        #endregion 账户支出信息
    }
}